package ru.spb.osll.myfit.server.handler;

import org.apache.log4j.Logger;
import ru.spb.osll.myfit.common.*;
import ru.spb.osll.myfit.server.Converter;
import ru.spb.osll.myfit.server.parser.PageParser;
import ru.spb.osll.myfit.server.parser.PageParserUtils;
import ru.spb.osll.myfit.server.parser.RecursiveIncludeException;
import ru.spb.osll.myfit.util.IOUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class PageLoader {
	private Logger LOG = Logger.getLogger(PageLoader.class);
	protected PageParser m_parser = new PageParser(new Converter());
	private String m_root;

	public PageLoader(String root) {
		m_root = root;
	}

	public Page loadPage(String fileName, String pagePath, String includePath)
			throws IOException {

		Page page = new Page();
		List<String> lines;
		try {
			lines = loadPage(fileName);
			List<PageElement> pageElements = m_parser
					.parsePage(new ArrayList<String>(lines));
			List<String> allPath = new ArrayList<String>();

			for (PageElement element : pageElements) {
				allPath.clear();
				if (!(element instanceof Include)
						&& !(element instanceof Content)) {
					page.insert(element);
				} else if (element instanceof Content) {
					Integer lv = ((Content) element).getLevel();
					Integer countLv = null;
					if (lv != null)
						countLv = 0;

					File file = new File(fileName);
					if (file.getName().equals("content.txt")) {
						TreeNode root = new TreeNode(file.getParentFile()
								.getName());
						((Content) element).setTreeNode(root);
						System.out.println("root: " + root.getReference());
						if (countLv != null)
							countLv++;
						File[] files = file.getParentFile().listFiles();
						for (int i = 0; i < files.length; i++) {
							if (files[i].isDirectory()) {
								setTree(root, files[i], lv, countLv);
							}
						}

						page.insert(element);
					}
				} else {
					allPath.add(pagePath);
					try {
						Page inludePage = createInlcudePage(((Include) element)
								.getPath(), allPath);
						((Include) element).setPage(inludePage);
						page.insert(element);
					} catch (RecursiveIncludeException exeption) {
						String message = TextParsingTools.RECURSIVE_CALL
								+ pagePath + " from the page "
								+ exeption.getMessage();
						page.insert(new IncludeError(((Include) element)
								.getPath(), createPageWithError(message)));
					} catch (FileNotFoundException e) {
						page
								.insert(new IncludeError(((Include) element)
										.getPath(), createPageWithError(e
										.getMessage())));
					}
				}
			}

			// add setUp in created page
			allPath.clear();
			allPath.add(pagePath);
			SetUp setUp = addSetUp(pagePath, allPath);
			if (setUp != null) {
				page.insertElement(setUp, 0);
			}

			// add TearDown in created page
			allPath.clear();
			allPath.add(pagePath);
			TearDown tearDown = addTearDown(pagePath, allPath);
			if (tearDown != null) {
				page.insert(tearDown);
			}
		} catch (FileNotFoundException e) {
			LOG.info("File not found: " + fileName + ". Load new page");
		}

		return page;
	}

	private SetUp addSetUp(String pagePath, List<String> allPath) {
		if (isSetUpOrTearDown(pagePath)) {
			return null;
		}

		String includePath;
		if (pagePath.contains(".")) {
			int beginIndex = pagePath.lastIndexOf(".");
			String rootPath = pagePath.substring(0, beginIndex + 1);
			includePath = rootPath + TextParsingTools.SETUP;
			if (!includePath.startsWith(".")) {
				includePath = "." + includePath;
			}
		} else {
			includePath = "." + TextParsingTools.SETUP;
		}

		try {
			Page page = createInlcudePage(includePath, allPath);
			return new SetUp(includePath, page);
		} catch (FileNotFoundException e) {
			LOG.debug("SetUp for page " + pagePath + " not found");
		} catch (RecursiveIncludeException e) {
			LOG.debug("SetUp for page " + pagePath + " isRecursive");
		}
		return null;
	}

	private TearDown addTearDown(String pagePath, List<String> allPath) {
		if (isSetUpOrTearDown(pagePath)) {
			return null;
		}

		String includePath;
		if (pagePath.contains(".")) {
			int beginIndex = pagePath.lastIndexOf(".");
			String rootPath = pagePath.substring(0, beginIndex + 1);
			includePath = rootPath + TextParsingTools.TEARDOWN;
			if (!includePath.startsWith(".")) {
				includePath = "." + includePath;
			}
		} else {
			includePath = "." + TextParsingTools.TEARDOWN;
		}

		try {
			Page page = createInlcudePage(includePath, allPath);
			return new TearDown(includePath, page);
		} catch (FileNotFoundException e) {
			LOG.debug("TearDown for page " + pagePath + " not found");
		} catch (RecursiveIncludeException e) {
			LOG.debug("TearDown for page " + pagePath + " isRecursive");
		}
		return null;
	}

	private boolean isSetUpOrTearDown(String pagePath) {
		String path;
		if (pagePath.contains(".")) {
			int beginIndex = pagePath.lastIndexOf(".");
			path = pagePath.substring(beginIndex + 1);
		} else {
			path = pagePath;
		}

		if (path.equals(TextParsingTools.SETUP)) {
			return true;
		}
		if (path.equals(TextParsingTools.TEARDOWN)) {
			return true;
		}

		return false;
	}

	public Page createPageWithError(String errorMessage) {
		Page result = new Page();
		result.insert(createPageError(errorMessage));

		return result;
	}

	private PageError createPageError(String msg) {
		PageError pageError = new PageError();
		pageError.setMessage(msg);
		return pageError;
	}

	public Page createInlcudePage(String includePath, List<String> allPath)
			throws RecursiveIncludeException, FileNotFoundException {
		Page page = new Page();
		if (isRecursiveInclude(includePath, allPath)) {
			allPath.add(includePath);
			throw new RecursiveIncludeException(getRecursivePath(allPath));
		} else {
			try {
				List<String> lines = loadPage(PageParserUtils.getFullPath(
						m_root, includePath));
				List<PageElement> pageElements = m_parser
						.parsePage(new ArrayList<String>(lines));
				for (PageElement element : pageElements) {

					if (!(element instanceof Include)) {
						page.insert(element);
					} else {
						ArrayList<String> forNextInclude = new ArrayList<String>(
								allPath);
						forNextInclude.add(includePath);
						try {
							Page inludePage = createInlcudePage(
									((Include) element).getPath(),
									forNextInclude);
							((Include) element).setPage(inludePage);
							page.insert(element);
						} catch (RecursiveIncludeException exeption) {
							throw exeption;
						} catch (FileNotFoundException e) {
							page.insert(new IncludeError(((Include) element)
									.getPath(), createPageWithError(e
									.getMessage())));
						}
					}
				}

				ArrayList<String> forSetUp = new ArrayList<String>(allPath);
				forSetUp.addAll(allPath);
				SetUp setUp = addSetUp(includePath, forSetUp);
				if (setUp != null) {
					page.insertElement(setUp, 0);
				}

				forSetUp.clear();
				forSetUp.addAll(allPath);
				TearDown tearDown = addTearDown(includePath, forSetUp);
				if (tearDown != null) {
					page.insert(tearDown);
				}

			} catch (FileNotFoundException e) {
				throw e;
			} catch (IOException e) {
				LOG.error("Can't read page ", e);
			}
		}
		return page;
	}

	private String getRecursivePath(List<String> allPath) {
		String result = new String();
		int size = allPath.size();
		for (int i = 1; i < size; ++i) {
			result += allPath.get(i);
			result += (i != size - 1) ? " ⇾ " : " ";
		}
		return result;
	}

	private boolean isRecursiveInclude(String includePath, List<String> allPaths) {
		if (allPaths.contains(includePath)) {
			return true;
		}

		if (includePath.startsWith(".")) {
			String pathWithoutPoint = includePath.substring(1);
			if (allPaths.contains(pathWithoutPoint)) {
				return true;
			}
		} else {
			String pathWithPoint = "." + includePath;
			if (allPaths.contains(pathWithPoint)) {
				return true;
			}
		}
		return false;
	}

	public List<String> loadPage(String fileName) throws IOException,
			FileNotFoundException {
		LOG.info("Loading page, path: " + fileName);
		FileInputStream stream = null;
		try {
			stream = new FileInputStream(fileName);
			BufferedReader inStream = new BufferedReader(new InputStreamReader(
					stream));
			return IOUtils.readLines(inStream);
		} finally {
			LOG.debug("file don't exist in finaly");
			if (stream != null)
				stream.close();
		}
	}

	public void setTree(TreeNode root, File file, Integer lv, Integer countLv) {
		boolean flag = true;
		if (file.isDirectory()) {
			if (countLv != null && lv != null) {
				if (countLv <= lv) {
					countLv++;
				} else
					flag = false;
			}
			if (flag) {
				String fileNames[] = file.list();
				List names = Arrays.asList(fileNames);
				if (names.contains("content.txt")) {
					TreeNode node = new TreeNode(file.getName());
					root.addChildNode(node);
					System.out.println("Parent: " + root.getReference());
					System.out.println("Children: " + node.getReference());
					File[] files = file.listFiles();
					for (int i = 0; i < files.length; i++) {
						if (files[i].isDirectory()) {
							setTree(node, files[i], lv, countLv);
						}
					}
				}
			}
		}
	}
}
